指向void的指针

  一个指向任何对象类型的指针都可以赋值给类型void*的变量,void*可以赋值给另一个void*,两个void*可以比较相等与否,而且可以显式地将void*转换到另一个类型。其他操作都是不安全的,因为编译器并不知道实际被指的是哪种对象。因此,对void*做其他任何操作都将引起编译错误。要使用void*,我们就必须显式地将它转换到某个指向特定类型的指针。例如,

    void f(int* pi)
    {
        void* pv = pi;        // 可以:从int* 到void*的隐式转换式
        *pv;                  // 错误❌:void*不能间接引用
        pv++;                 // 错误❌:void*不能增量(不知道被指对象的大小)
        int* pi2 = static_cast<int*>(pv);        // 显式转换回int*

        double* pd1 = pv;     // 错误❌
        double* pd2 = pi;     // 错误❌
        double* pd3 = static_cast<double*>(pv);    // 不安全
    }

一般来说,如果一个指针被转换(“强制”,cast)到与被指对象的实际类型不同的指针,使用后一个指针就是不安全的。例如,一台机器可能假设每个double被分配了8个字节,如果真是这样的话,那么,若被pi所指的int不是这样分配就会产生奇怪的行为。这种形式的显式类型转换,从本质上说就是不安全的、丑陋的,所以这里所用的记法static_cast,也被设计成极其丑陋的样子。

  void*的最重要用途是需要向函数传递一个指针,而又不能对对象的类型做任何假设。还有就是从函数返回一个无类型的对象。要使用这样的对象,必须通过显式的类型转换。

  采用void*的函数通常存在于系统中很低的层次里,在那里需要操作某些真实的硬件资源。例如,

void* my_alloc(size_t n);        // 从我特定的堆中分配n字节的存储

在系统中较高层次上出现void*应该认为是可疑的,它们就像是设计错误的指示器。在那些为优化而使用的地方,可以将void*隐藏在类型安全的界面之后(13.5节、24.4.2节)。

  到函数的指针(7.7节)和到成员的指针(15.5节)都不能赋给void*。

🔚